﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Messaging;
using System.Net.Sockets;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using Comunication;
using ComunicationServer.TCPCommands;
using TripRemoteObject;

namespace ComunicationServer
{
    public class ComunicationServerController
    {

        #region Declarations

        private object ClientsLock = new object();
        private IDictionary<int, Client> clients = new Dictionary<int, Client>();
        readonly string QueueComunicationClientSend = Properties.Settings.Default.QueueComunicationClientSend;
        readonly string QueueComunicationClientReceive = Properties.Settings.Default.QueueComunicationClientReceive;
        private TcpChannel chan;

        #endregion

        #region Singleton
        static readonly ComunicationServerController instance = new ComunicationServerController();

        static ComunicationServerController()
        {
        }

        ComunicationServerController()
        {
        }

        public static ComunicationServerController Instance
        {
            get
            {
                return instance;
            }
        }

        #endregion

        #region Clients Access

        public void AddClient(int clientId, Client newClient)
        {
            lock (ClientsLock)
            {
                Client client;
                if (!clients.TryGetValue(clientId, out client))
                {
                    Logger.Log(string.Format("Client {0} added", clientId));
                    clients.Add(clientId, newClient);
                }
                else
                {
                    Logger.Log(string.Format("Client already exists: {0}", clientId));
                }
            }
        }

        internal Client GetClient(int userId)
        {
            lock (ClientsLock)
            {
                return clients[userId];
            }
        }

        public void RemoveClient(int clientId)
        {
            lock (ClientsLock)
            {
                clients.Remove(clientId);
                Logger.Log(string.Format("Client {0} removed", clientId));
            }
        }

        #endregion

        public void SendMessageQueue(int currentClientId)
        {
            MessageQueue msMq;
            msMq = new MessageQueue(QueueComunicationClientSend);
            try
            {
                var operationMessage = currentClientId;
                msMq.Send(operationMessage.ToString());
                Logger.Log("Send client message to Client Admin Server, clientID:" + currentClientId);
            }
            catch (MessageQueueException ee)
            {
                Logger.Log(string.Format("Error sending client message to Client Admin Server, clientID: {0} / error: {1}", currentClientId, ee.Message));
            }
            catch (Exception eee)
            {
                Logger.Log(string.Format("Error sending client message to Client Admin Server, clientID: {0} / error: {1}", currentClientId, eee.Message));
            }
            finally
            {
                msMq.Close();
            }

        }

        public void ReadQueue()
        {
            MessageQueue msMqReceived = new MessageQueue(QueueComunicationClientReceive);
            try
            {
                msMqReceived.Formatter = new XmlMessageFormatter(new[] { typeof(string) });
                msMqReceived.ReceiveCompleted += MyReceiveCompleted;
                msMqReceived.Purge();
                msMqReceived.BeginReceive();
            }
            catch (MessageQueueException messageQueue)
            {
                Logger.Log(messageQueue.ToString());
            }
            catch (Exception ex)
            {
                Logger.Log(ex.ToString());
            }
            finally
            {
                //TODO:probar esto
                msMqReceived.Close();
            }
        }

        private void MyReceiveCompleted(object source, ReceiveCompletedEventArgs asyncResult)
        {
            Logger.Log(string.Format("Start receive from queue."));

            MessageQueue msMqReceived = (MessageQueue)source;
            try
            {
                msMqReceived.Formatter = new XmlMessageFormatter(new[] { typeof(string) });
                Message message = msMqReceived.EndReceive(asyncResult.AsyncResult);

                //================== realizar operacion sobre el mensaje =============================
                if (message != null && message.Body != null)
                {
                    var result = message.Body.ToString().Split('|');
                    if (result.Count() == 2)
                    {
                        int userId = Convert.ToInt32(result[0]);
                        bool habilitado = Convert.ToBoolean(result[1]);
                        Socket handler = GetClient(userId).Handler;
                        TCPClient.SendCommand(handler, new TakeVehicleResponse(habilitado));

                        Logger.Log(string.Format("Message received from Client Admin Server Client {0} result {1}", userId, habilitado));
                    }
                }
                //====================================================================================
            }
            catch (MessageQueueException ex)
            {
                Logger.Log(ex.Message);
            }
            finally
            {
                msMqReceived.BeginReceive();
            }
        }

        public void SendClientResponse(int userId,AmountState amountState)
        {
            Socket handler = GetClient(userId).Handler;
            if (amountState == AmountState.NoCredit)
            {
                TCPClient.SendCommand(handler, new OutOfCredit());
                Logger.Log(string.Format("Client {0} out of credit", userId));
            }
            else if (amountState == AmountState.LowBalance)
            {
                TCPClient.SendCommand(handler, new LowBalance());
                Logger.Log(string.Format("Client {0} with low balance on account", userId));
            }
        }

        public void StartRemoting()
        {
            chan = new TcpChannel();
            ChannelServices.RegisterChannel(chan, false);
        }
    }
}
